{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8ac6762e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'1.4.2'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import sklearn\n",
    "sklearn.__version__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "01275742",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[nltk_data] Downloading package stopwords to /home/user/nltk_data...\n",
      "[nltk_data]   Package stopwords is already up-to-date!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import sys\n",
    "import glob\n",
    "import numpy as np\n",
    "from tqdm import tqdm\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import requests\n",
    "import random\n",
    "from collections import Counter\n",
    "from striprtf.striprtf import rtf_to_text\n",
    "from sklearn.metrics import f1_score, accuracy_score\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.model_selection import train_test_split\n",
    "import nltk\n",
    "from nltk.corpus import stopwords\n",
    "from sklearn.utils.class_weight import compute_class_weight\n",
    "nltk.download('stopwords')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "0e046a79",
   "metadata": {},
   "outputs": [],
   "source": [
    "def remove_russian_stopwords(text):\n",
    "    russian_stopwords = stopwords.words(\"russian\")\n",
    "    words = text.split()\n",
    "    filtered_words = [word for word in words if word.lower() not in russian_stopwords]\n",
    "    return ' '.join(filtered_words)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "7cae9231",
   "metadata": {},
   "outputs": [],
   "source": [
    "MAPPING = {\n",
    "    \"proxy\": \"доверенность\",\n",
    "    \"contract\": \"договор\",\n",
    "    \"act\": \"акт\",\n",
    "    \"application\": \"заявление\",\n",
    "    \"order\": \"приказ\",\n",
    "    \"invoice\": \"счет\",\n",
    "    \"bill\": \"приложение\",\n",
    "    \"arrangement\": \"соглашение\",\n",
    "    \"contract offer\": \"договор оферты\",\n",
    "    \"statute\": \"устав\",\n",
    "    \"determination\": \"решение\",\n",
    "}\n",
    "etor = MAPPING\n",
    "rtoe = {value: key for key, value in MAPPING.items()}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "d1cba80f",
   "metadata": {},
   "outputs": [],
   "source": [
    "CLASSES_= list(MAPPING.values())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2add0d65",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv('../data/lemma_sample.csv', index_col=0)\n",
    "data['text'] = data['text'].apply(lambda x: remove_russian_stopwords(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "5929dd79",
   "metadata": {},
   "outputs": [],
   "source": [
    "for class_ in CLASSES_:\n",
    "    data[class_] = data['text'].str.contains(class_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "1d45cb93",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "class\n",
       "proxy             71\n",
       "contract          70\n",
       "act               69\n",
       "application       61\n",
       "order             50\n",
       "invoice           43\n",
       "bill              41\n",
       "arrangement       40\n",
       "contract offer    25\n",
       "statute           21\n",
       "determination     10\n",
       "Name: count, dtype: int64"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data['class'].value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "856e67ec",
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data, test_data = train_test_split(\n",
    "    data,\n",
    "    test_size=0.25,\n",
    "    random_state=42,\n",
    "    stratify=data[\"class\"],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67f5cd5b",
   "metadata": {},
   "source": [
    "--- \n",
    "## Фичи - названия классов в тексте"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "b118657d",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = RandomForestClassifier(random_state=42)\n",
    "model.fit(train_data[CLASSES_], train_data['class'])\n",
    "\n",
    "y_true = test_data['class']\n",
    "y_pred = model.predict(test_data[CLASSES_])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "6ec79a67",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "acc:  0.7937\n",
      "f1 :  0.7423\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(\n",
    "    f'acc: {accuracy_score(y_true, y_pred): .4f}\\n'\n",
    "    f'f1 : {f1_score(y_true, y_pred, average=\"macro\"): .4f}\\n'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47872851",
   "metadata": {},
   "source": [
    "--- \n",
    "## Статистика по встречаемости"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "f032b9f5",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 11/11 [00:00<00:00, 2498.10it/s]\n"
     ]
    }
   ],
   "source": [
    "freq = Counter(' '.join(train_data['text'].values.tolist()).split())\n",
    "stat = train_data.groupby('class', as_index=False).agg(\n",
    "    {'text': lambda x: ' '.join(x)}\n",
    ")\n",
    "stat['words'] = stat['text'].apply(lambda x: Counter(x.split()))\n",
    "stat['top-10'] = stat['words'].apply(lambda x: x.most_common(10))\n",
    "\n",
    "metric = {}\n",
    "for class_ in tqdm(CLASSES_):\n",
    "\n",
    "    value = {}\n",
    "    for word, fr in stat[\n",
    "        stat['class'] == rtoe[class_]\n",
    "    ]['top-10'].values.tolist()[0]:\n",
    "        value[word] = fr / freq[word]\n",
    "        \n",
    "    metric[class_] = dict(sorted(value.items(), key=lambda item: item[1], reverse=True))\n",
    "    \n",
    "value = pd.DataFrame(metric.items(), columns=['class', 'value'])\n",
    "value['class'] = value['class'].replace(rtoe)\n",
    "stat = stat.merge(value, on='class')\n",
    "stat = stat.set_index('class')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "d8583243",
   "metadata": {},
   "outputs": [],
   "source": [
    "stat.to_csv('../data/stat.csv', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "437b3deb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "доверенность {'доверенность': 0.7077625570776256, 'зарегистрировать': 0.6747967479674797, 'выдать': 0.6549019607843137, 'паспорт': 0.5522388059701493, 'право': 0.2463768115942029, 'документ': 0.2159090909090909, 'весь': 0.20649651972157773, 'год': 0.20359281437125748, 'адрес': 0.1806083650190114, 'лицо': 0.10810810810810811}\n",
      "\n",
      "договор {'заказчик': 0.8075117370892019, 'далее': 0.5704697986577181, 'работа': 0.5390243902439025, 'установить': 0.496, 'оплата': 0.4229934924078091, 'соглашение': 0.4058679706601467, 'сторона': 0.3934142114384749, 'акт': 0.33687002652519893, 'договор': 0.2735166425470333, 'настоящий': 0.17380025940337224}\n",
      "\n",
      "акт {'комиссия': 0.7045454545454546, 'составить': 0.5769230769230769, 'акт': 0.4270557029177719, 'год': 0.18962075848303392, 'директор': 0.16382252559726962, 'сторона': 0.10918544194107452, 'подпись': 0.09683794466403162, 'настоящий': 0.08430609597924774, 'лицо': 0.07396870554765292, 'договор': 0.06222865412445731}\n",
      "\n",
      "заявление {'просить': 1.0, 'заявление': 0.4503311258278146, 'суд': 0.4461538461538462, 'регистрация': 0.15, 'решение': 0.13253012048192772, 'адрес': 0.12927756653992395, 'место': 0.12539184952978055, 'паспорт': 0.1044776119402985, 'год': 0.03792415169660679, 'договор': 0.013748191027496382}\n",
      "\n",
      "приказ {'приказывать': 1.0, 'приказ': 0.8103448275862069, 'ознакомить': 0.6964285714285714, 'контроль': 0.6521739130434783, 'отдел': 0.3548387096774194, 'руководитель': 0.23529411764705882, 'год': 0.16167664670658682, 'исполнение': 0.1346938775510204, 'директор': 0.10921501706484642, 'подпись': 0.09090909090909091}\n",
      "\n",
      "счет {'масса': 0.9916666666666667, 'груз': 0.9634703196347032, 'валюта': 0.8079470198675497, 'тариф': 0.7333333333333333, 'руб': 0.5123152709359606, 'код': 0.4536082474226804, 'номер': 0.38113207547169814, 'подпись': 0.35177865612648224, 'место': 0.3448275862068966, 'наименование': 0.2962962962962963}\n",
      "\n",
      "приложение {'имущественный': 0.9080459770114943, 'покупатель': 0.45698924731182794, 'код': 0.35051546391752575, 'наименование': 0.2895622895622896, 'товар': 0.2863247863247863, 'адрес': 0.1920152091254753, 'весь': 0.18097447795823665, 'услуга': 0.13713405238828968, 'право': 0.1288244766505636, 'работа': 0.10121951219512196}\n",
      "\n",
      "соглашение {'электронный': 0.4260355029585799, 'соглашение': 0.42053789731051344, 'именовать': 0.2742616033755274, 'сторона': 0.2512998266897747, 'документ': 0.18863636363636363, 'действовать': 0.18241042345276873, 'год': 0.12574850299401197, 'настоящий': 0.1219195849546044, 'договор': 0.1085383502170767, 'лицо': 0.08392603129445235}\n",
      "\n",
      "договор оферты {'оферта': 0.9341692789968652, 'услуга': 0.7642526964560863, 'случай': 0.6217765042979942, 'товар': 0.6082621082621082, 'условие': 0.5991983967935872, 'указать': 0.56973293768546, 'настоящий': 0.4980544747081712, 'договор': 0.4573082489146165, 'оплата': 0.41431670281995664, 'лицо': 0.30014224751066854}\n",
      "\n",
      "устав {'доля': 0.9342105263157895, 'федеральный': 0.8913043478260869, 'участник': 0.8865979381443299, 'уставный': 0.8840579710144928, 'закон': 0.7441860465116279, 'общество': 0.7420924574209246, 'часть': 0.7413793103448276, 'ограничить': 0.4675925925925926, 'ответственность': 0.26244343891402716, 'лицо': 0.14935988620199148}\n",
      "\n",
      "решение {'единственный': 0.37777777777777777, 'дом': 0.24444444444444444, 'серия': 0.16176470588235295, 'решение': 0.09036144578313253, 'участник': 0.059278350515463915, 'паспорт': 0.05472636815920398, 'директор': 0.04778156996587031, 'выдать': 0.043137254901960784, 'номер': 0.04150943396226415, 'год': 0.02594810379241517}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for class_ in CLASSES_:\n",
    "#     print(class_, stat.loc[rtoe[class_], 'top-10'])\n",
    "    print(class_, stat.loc[rtoe[class_], 'value'])\n",
    "    print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "21dc5bc8",
   "metadata": {},
   "outputs": [],
   "source": [
    "top_words_ = [\n",
    "    list(dct.keys()) for dct in stat['value'].values.tolist()\n",
    "]\n",
    "top_words = []\n",
    "for lst in top_words_:\n",
    "    top_words.extend(lst)\n",
    "    \n",
    "top_words = [i for i in set(top_words) if len(i) > 2]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e65f24c",
   "metadata": {},
   "source": [
    "--- \n",
    "## Фичи по встречаемости"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "60222f78",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv('../data/lemma_sample.csv', index_col=0)\n",
    "data['text'] = data['text'].apply(lambda x: remove_russian_stopwords(x))\n",
    "\n",
    "for top in top_words:\n",
    "    data[top] = data['text'].str.findall(top).str.len()\n",
    "\n",
    "train_data, test_data = train_test_split(\n",
    "    data,\n",
    "    test_size=0.25,\n",
    "    random_state=42,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "8d952369",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "model = RandomForestClassifier(random_state=42)\n",
    "model.fit(train_data[top_words], train_data['class'])\n",
    "\n",
    "y_true = test_data['class']\n",
    "y_pred = model.predict(test_data[top_words])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "0ba508db",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "acc:  0.9683\n",
      "f1 :  0.8706\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(\n",
    "    f'acc: {accuracy_score(y_true, y_pred): .4f}\\n'\n",
    "    f'f1 : {f1_score(y_true, y_pred, average=\"macro\"): .4f}\\n'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69ce685d",
   "metadata": {},
   "source": [
    "--- \n",
    "\n",
    "## Добавим веса классов"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "0bba0b97",
   "metadata": {},
   "outputs": [],
   "source": [
    "weights = compute_class_weight(\n",
    "    class_weight=\"balanced\", \n",
    "    classes=train_data[\"class\"].unique(), \n",
    "    y=train_data[\"class\"],\n",
    ")\n",
    "\n",
    "weights_dict = dict(zip(\n",
    "    train_data[\"class\"].unique(), \n",
    "    weights\n",
    "))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "1f97f807",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = RandomForestClassifier(class_weight=weights_dict, random_state=42)\n",
    "model.fit(train_data[top_words], train_data['class'])\n",
    "\n",
    "y_true = test_data['class']\n",
    "y_pred = model.predict(test_data[top_words])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "caba32b2",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "acc:  0.9444\n",
      "f1 :  0.8500\n"
     ]
    }
   ],
   "source": [
    "print(\n",
    "    f'acc: {accuracy_score(y_true, y_pred): .4f}\\n'\n",
    "    f'f1 : {f1_score(y_true, y_pred, average=\"macro\"): .4f}'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad8ce89a",
   "metadata": {},
   "source": [
    "---\n",
    "## Расширенный датасет"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "8f761c6d",
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset_path = '../data/lemma_sample.csv'\n",
    "external_dataset_path = '../data/lemma_external_with_samplelabel.csv'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "2df0b14d",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv(dataset_path, index_col=0)\n",
    "external_data = pd.read_csv(\n",
    "    external_dataset_path, index_col=0\n",
    ")\n",
    "# external_data['clear_text_in_sample'] = (\n",
    "#     pd.read_csv('../data/external_with_samplelabel.csv')\n",
    "#     ['clear_text_in_sample']\n",
    "# )\n",
    "\n",
    "external_data = external_data[\n",
    "    external_data[\"clear_text_in_sample\"] == 0][[\"class\", \"text\"]]\n",
    "data = pd.concat([data, external_data])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "dbf2372b",
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data, test_data = train_test_split(\n",
    "    data,\n",
    "    test_size=0.25,\n",
    "    random_state=42,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "5b6d880c",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 11/11 [00:00<00:00, 957.15it/s]\n"
     ]
    }
   ],
   "source": [
    "freq = Counter(' '.join(train_data['text'].values.tolist()).split())\n",
    "stat = train_data.groupby('class', as_index=False).agg(\n",
    "    {'text': lambda x: ' '.join(x)}\n",
    ")\n",
    "stat['words'] = stat['text'].apply(lambda x: Counter(x.split()))\n",
    "stat['top-10'] = stat['words'].apply(lambda x: x.most_common(10))\n",
    "\n",
    "metric = {}\n",
    "for class_ in tqdm(CLASSES_):\n",
    "\n",
    "    value = {}\n",
    "    for word, fr in stat[\n",
    "        stat['class'] == rtoe[class_]\n",
    "    ]['top-10'].values.tolist()[0]:\n",
    "        value[word] = fr / freq[word]\n",
    "        \n",
    "    metric[class_] = dict(sorted(value.items(), key=lambda item: item[1], reverse=True))\n",
    "    \n",
    "value = pd.DataFrame(metric.items(), columns=['class', 'value'])\n",
    "value['class'] = value['class'].replace(rtoe)\n",
    "stat = stat.merge(value, on='class')\n",
    "stat = stat.set_index('class')\n",
    "\n",
    "top_words_ = [\n",
    "    list(dct.keys()) for dct in stat['value'].values.tolist()\n",
    "]\n",
    "top_words = []\n",
    "for lst in top_words_:\n",
    "    top_words.extend(lst)\n",
    "    \n",
    "top_words = sorted([i for i in set(top_words) if len(i) > 2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "99890e23",
   "metadata": {},
   "outputs": [],
   "source": [
    "for top in top_words:\n",
    "    data[top] = data['text'].str.findall(top).str.len()\n",
    "    \n",
    "train_data, test_data = train_test_split(\n",
    "    data,\n",
    "    test_size=0.25,\n",
    "    random_state=42,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "001ebe6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = RandomForestClassifier(\n",
    "    class_weight=weights_dict, random_state=42\n",
    ")\n",
    "model.fit(train_data[top_words], train_data['class'])\n",
    "\n",
    "y_true = test_data['class']\n",
    "y_pred = model.predict(test_data[top_words])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "c72b4b63",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>адрес</th>\n",
       "      <th>акт</th>\n",
       "      <th>бальмонт</th>\n",
       "      <th>валюта</th>\n",
       "      <th>весь</th>\n",
       "      <th>выдать</th>\n",
       "      <th>год</th>\n",
       "      <th>город</th>\n",
       "      <th>груз</th>\n",
       "      <th>дата</th>\n",
       "      <th>...</th>\n",
       "      <th>товар</th>\n",
       "      <th>требовать</th>\n",
       "      <th>указать</th>\n",
       "      <th>условие</th>\n",
       "      <th>услуга</th>\n",
       "      <th>устав</th>\n",
       "      <th>уставный</th>\n",
       "      <th>участник</th>\n",
       "      <th>часть</th>\n",
       "      <th>электронный</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>104</th>\n",
       "      <td>0</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>163</th>\n",
       "      <td>4</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>5</td>\n",
       "      <td>5</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>199</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>325</th>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>266</th>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>657</th>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>4</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>876</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>387</th>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1041</th>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>653</th>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>3</td>\n",
       "      <td>4</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>1350 rows × 71 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      адрес  акт  бальмонт  валюта  весь  выдать  год  город  груз  дата  ...  \\\n",
       "104       0    4         0       0     0       0    1      0     0     0  ...   \n",
       "163       4    3         0       0     5       5    2      0     0     1  ...   \n",
       "199       1    2         0       0     0       0    1      0     0     0  ...   \n",
       "325       2    0         0       0     0       0    1      0     0     0  ...   \n",
       "266       1    5         0       0     0       0    0      0     0     0  ...   \n",
       "...     ...  ...       ...     ...   ...     ...  ...    ...   ...   ...  ...   \n",
       "657       2    2         0       0     1       2    4      4     0     0  ...   \n",
       "876       1    4         0       0     0       0    0      0     0     0  ...   \n",
       "387       1    0         0       0     0       0    1      1     0     0  ...   \n",
       "1041      0    0         0       0     0       0    0      0     0     0  ...   \n",
       "653       2    2         0       0     0       3    4      4     0     0  ...   \n",
       "\n",
       "      товар  требовать  указать  условие  услуга  устав  уставный  участник  \\\n",
       "104       0          1        1        3       0      1         0         0   \n",
       "163       0          0        0        1       0      0         0         0   \n",
       "199       0          2        0        0       0      1         0         0   \n",
       "325       0          3        1        2       0      0         0         0   \n",
       "266       0          0        1        0       0      2         0         0   \n",
       "...     ...        ...      ...      ...     ...    ...       ...       ...   \n",
       "657       0          2        1        0       0      0         0         0   \n",
       "876       0          1        0        0       0      2         0         0   \n",
       "387       0          3        0        0       2      0         0         0   \n",
       "1041      0          0        0        0       0      0         0         0   \n",
       "653       0          2        1        0       0      0         0         0   \n",
       "\n",
       "      часть  электронный  \n",
       "104       0            0  \n",
       "163       0            0  \n",
       "199       0            0  \n",
       "325       0            0  \n",
       "266       0            0  \n",
       "...     ...          ...  \n",
       "657       0            0  \n",
       "876       0            0  \n",
       "387       0            0  \n",
       "1041      1            0  \n",
       "653       0            0  \n",
       "\n",
       "[1350 rows x 71 columns]"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_data[top_words]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "38667474",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "acc:  0.9733\n",
      "f1 :  0.9039\n"
     ]
    }
   ],
   "source": [
    "print(\n",
    "    f'acc: {accuracy_score(y_true, y_pred): .4f}\\n'\n",
    "    f'f1 : {f1_score(y_true, y_pred, average=\"macro\"): .4f}'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "b5eada51",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['../data/model.sav']"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import joblib\n",
    "filename = '../data/model.sav'\n",
    "joblib.dump(model, filename)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7bf844c3",
   "metadata": {},
   "source": [
    "---\n",
    "## Инференс"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "8fefc38e",
   "metadata": {},
   "outputs": [],
   "source": [
    "stat.to_csv('../data/stat.csv', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "d80cd5fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import joblib\n",
    "\n",
    "def predict(\n",
    "    test_df: pd.DataFrame, \n",
    "    model_path: str = '../data/model.sav'\n",
    ") -> list[str]:\n",
    "    \"\"\"\n",
    "    Inference\n",
    "    \n",
    "    :param test_df: columns - [['text']]. Should be processed!\n",
    "    \n",
    "    :return: list[class_]\n",
    "    \"\"\"\n",
    "\n",
    "    loaded_model = joblib.load(model_path)\n",
    "    \n",
    "    top_words = [\n",
    "        \"адрес\", \"акт\", \"бальмонт\", \"валюта\", \"весь\", \"выдать\", \"год\", \n",
    "        \"город\", \"груз\", \"дата\", \"действовать\", \"деятельность\", \"директор\", \n",
    "        \"доверенность\", \"договор\", \"документ\", \"доля\", \"единственный\", \"закон\", \n",
    "        \"зарегистрировать\", \"заявление\", \"или\", \"именовать\", \"имущественный\", \"код\", \n",
    "        \"комиссия\", \"контроль\", \"лицо\", \"масса\", \"место\", \"мой\", \"наименование\", \n",
    "        \"настоящий\", \"номер\", \"общество\", \"ограничить\", \"ознакомить\", \"оплата\", \n",
    "        \"ответственность\", \"отдел\", \"оферта\", \"паспорт\", \"подпись\", \"покупатель\", \n",
    "        \"право\", \"при\", \"приказ\", \"приказывать\", \"просить\", \"работа\", \"работник\", \n",
    "        \"регистрация\", \"решение\", \"руб\", \"случай\", \"соглашение\", \"составить\", \n",
    "        \"составлять\", \"срок\", \"сторона\", \"сумма\", \"товар\", \"требовать\", \"указать\", \n",
    "        \"условие\", \"услуга\", \"устав\", \"уставный\", \"участник\", \"часть\", \"электронный\",\n",
    "    ]\n",
    "    \n",
    "    for top in top_words:\n",
    "        test_df[top] = test_df['text'].str.findall(top).str.len()\n",
    "    \n",
    "    from IPython.display import display\n",
    "\n",
    "    test_df = test_df.drop(columns=['text'])\n",
    "    display(test_df)\n",
    "    preds = loaded_model.predict(test_df[top_words]).tolist()\n",
    "    return preds\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "b0e06ba9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>адрес</th>\n",
       "      <th>акт</th>\n",
       "      <th>бальмонт</th>\n",
       "      <th>валюта</th>\n",
       "      <th>весь</th>\n",
       "      <th>выдать</th>\n",
       "      <th>год</th>\n",
       "      <th>город</th>\n",
       "      <th>груз</th>\n",
       "      <th>дата</th>\n",
       "      <th>...</th>\n",
       "      <th>товар</th>\n",
       "      <th>требовать</th>\n",
       "      <th>указать</th>\n",
       "      <th>условие</th>\n",
       "      <th>услуга</th>\n",
       "      <th>устав</th>\n",
       "      <th>уставный</th>\n",
       "      <th>участник</th>\n",
       "      <th>часть</th>\n",
       "      <th>электронный</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>1 rows × 71 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "   адрес  акт  бальмонт  валюта  весь  выдать  год  город  груз  дата  ...  \\\n",
       "0      0    0         0       0     0       0    0      0     0     0  ...   \n",
       "\n",
       "   товар  требовать  указать  условие  услуга  устав  уставный  участник  \\\n",
       "0      0          0        0        0       0      0         0         0   \n",
       "\n",
       "   часть  электронный  \n",
       "0      0            0  \n",
       "\n",
       "[1 rows x 71 columns]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "['application']"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_text = \"Директору ООО «Кошкин дом» Денисову Олегу Викторовичу \\n От оператора горячей линии Бальмонт Сергея Валерьевича \\n Заявление о переводе на полную ставку\\n По условиям трудового договора от 01.03.2025 г. я работаю на 0.5 ставки, по 4 часа в день. В связи с появлением большого количества рабочего времени, прошу с 01.10.2025 г. перевести меня на полную ставку.28 сентября 2025 г. Бальмонт Сергей Валерьевич  \"\n",
    "test_df = pd.DataFrame({'text': [test_text]})\n",
    "predict(test_df)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
